【GitHub Actions】cdk-nagのテスト結果をPRコメントするワークフローを作成してみた
cdk-nag を利用していると、以下のような悩みが出てこないでしょうか。
- cdk-nag のセキュリティチェック結果を、プルリクエストのコメントで確認したい
- エラーやワーニングの修正点が整理された状態で確認したい
私も cdk-nag のテストを GitHub Actions を動かしていたのですが、どのようなエラーが発生したのかを Actions のページで確認していました。
これがメンバーごと行うことを考えると、効率的ではないですよね。
そんな悩みを解決するため本記事では、プルリクエストを作成した際にテスト結果をコメントするワークフローを作成してみたので紹介します。
cdk-nag の詳細については、以下ブログを参照ください。
前提
- cdk-nag のテストが
npm run test
で動作する - cdk-nag の出力メッセージを整形している
先述したブログが前提となっていますので、こちらを参照して設定してください。
作成したワークフロー
先に作成した GitHub Actions のワークフローを載せておきます。
こちらのコードは以下のリポジトリでも確認できます。
name: cdk-nag Test
on:
pull_request:
branches: [main]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js 20
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run tests
id: run-tests
continue-on-error: true
run: |
npm test 2>&1 | tee test-output.log
exit_code=${PIPESTATUS[0]}
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
exit $exit_code
- name: Create GitHub Comment
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs');
const testOutput = fs.readFileSync('test-output.log', 'utf8');
const errorMatches = testOutput.match(/\[error at .+?\] .+?(?=\n|$)/g);
const warningMatches = testOutput.match(/\[warning at .+?\] .+?(?=\n|$)/g);
let commentBody;
if (errorMatches) {
commentBody = `### 🚫 cdk-nag セキュリティチェック結果:対応が必要な問題があります
以下の項目について修正が必要です:
${errorMatches.map(error => `- ${error}`).join('\n')}
上記の問題点について、セキュリティとベストプラクティスの観点から修正してください。`;
}
if (warningMatches) {
const warningSection = `### ⚠️ cdk-nag セキュリティチェック結果:確認推奨事項
以下の項目について、確認してください:
${warningMatches.map(warning => `- ${warning}`).join('\n')}
これらの警告は必ずしも修正が必要というわけではありませんが、
より安全なインフラ構築のため、可能な範囲での対応を検討してください。`;
commentBody = commentBody
? `${commentBody}\n\n${warningSection}`
: warningSection;
}
if (!errorMatches && !warningMatches) {
commentBody = `### ✅ cdk-nag セキュリティチェック結果:問題なし
セキュリティチェックが完了し、問題は検出されませんでした。`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
- name: Fail if tests failed
if: steps.run-tests.outputs.exit_code != 0
run: exit 1
実行結果例
プルリクエストを作成すると、以下のようなコメントがされます。
成功
エラー
ワーニング
エラーとワーニング
詳細
ワークフローの詳細について簡単に解説します。
テストの実行結果保存
以下のステップでnpm test
を実行し、その結果をtest-output.log
に保存しています。
- name: Run tests
id: run-tests #あとで参照するためのID
continue-on-error: true #エラーでも後続処理を実行
run: |
npm test 2>&1 | tee test-output.log
exit_code=${PIPESTATUS[0]}
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
exit $exit_code
PIPESTATUS
は各コマンドの終了ステータスを含む配列になっています。今回はnpm test
の終了ステータスのみが入っており、[0]を指定することで終了コードを取得しています。
この終了コードは後続で使用したいので、$GITHUB_OUTPUT
の環境変数へ保存します。
テストの成功、失敗を反映させるためexit $exit_code
としていますが、continue-on-error: true
を設定しているためエラーでも後続処理が実行されます。
コメント追加
プルリクエストに対してコメントを追加するステップです。
先ほど保存したテストの実行結果を読み取り、内容に合わせてコメントを作成します。
コメントについては、適宜環境に合わせて修正してください。
- name: Create GitHub Comment
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs');
const testOutput = fs.readFileSync('test-output.log', 'utf8');
const errorMatches = testOutput.match(/\[error at .+?\] .+?(?=\n|$)/g);
const warningMatches = testOutput.match(/\[warning at .+?\] .+?(?=\n|$)/g);
let commentBody;
if (errorMatches) {
commentBody = `### 🚫 cdk-nag セキュリティチェック結果:対応が必要な問題があります
以下の項目について修正が必要です:
${errorMatches.map(error => `- ${error}`).join('\n')}
上記の問題点について、セキュリティとベストプラクティスの観点から修正してください。`;
}
if (warningMatches) {
const warningSection = `### ⚠️ cdk-nag セキュリティチェック結果:確認推奨事項
以下の項目について、確認してください:
${warningMatches.map(warning => `- ${warning}`).join('\n')}
これらの警告は必ずしも修正が必要というわけではありませんが、
より安全なインフラ構築のため、可能な範囲での対応を検討してください。`;
commentBody = commentBody
? `${commentBody}\n\n${warningSection}`
: warningSection;
}
if (!errorMatches && !warningMatches) {
commentBody = `### ✅ cdk-nag セキュリティチェック結果:問題なし
セキュリティチェックが完了し、問題は検出されませんでした。`;
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: commentBody
});
errorMatches
とwarningMatches
はそれぞれ正規表現を使って検索しています。ここで検索している文字列は整形後の出力となるため、デフォルトの cdk-nag の出力では動作しない点に注意してください。
その後のlet commentBody;
以下はマッチした文字列に合わせてコメントを作成しています。
最後に作成したコメントを body に設定してプルリクエストに対しコメントします。
テスト失敗時のエラーハンドリング
最後に cdk-nag のテストで失敗している場合に、このワークフロー自体も失敗にしています。npm test
の終了コードが 0 以外、つまりエラーがあった場合はエラーコード 1 で終了します。
- name: Fail if tests failed
if: steps.run-tests.outputs.exit_code != 0
run: exit 1
前のステップ(Run tests)でcontinue-on-error: true
としているため、このコードがない場合は必ず正常終了になってしまいます。そのため、ここで出力された終了コードを参照することで、ワークフロー全体の成功・失敗をハンドリングしています。
そのため、テストで失敗したコードがマージされることを防ぐことができます。
注意点
今回は検証のためnpm test
で cdk-nag を実行しています。もし他のテスト(CDK のスナップショット等)がある場合は、npm script を分割して、別々のステップで実行するように修正することをお勧めします。
以下はスナップショットと実行コマンドを分ける例です。
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"test": "jest",
"test:cdk-nag": "jest test/cdk-nag",
"test:snap": "jest test/snapshot",
"cdk": "cdk"
},
まとめ
cdk-nag のテスト結果をプルリクエストにコメントするワークフローを紹介してみました。
プルリクエストにコメントすることで、開発者がスムーズにエラー内容を把握できます。ぜひ開発フローに取り入れてみてください。